package cn.donting.jme.edit.context.input;

import com.jme3.cursors.plugins.JmeCursor;
import com.jme3.input.MouseInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * fx 的鼠标输入
 * 参考 AWTMouseInput
 * @see com.jme3.input.AWTMouseInput
 */
@Slf4j
public class FxMouseInput implements EventHandler<MouseEvent> ,MouseInput {
    private Pane fxPane;
    private RawInputListener listener;
    private static final int WHEEL_SCALE = 10;

    private boolean initialize=false;
    private EventHandler<ScrollEvent> eventScrollHandler;
    /**
     *事件队列
     */
    private final LinkedList<MouseMotionEvent> mouseMotionEvents=new LinkedList<>();
    private final LinkedList<MouseButtonEvent> mouseButtonEvents=new LinkedList<>();


    private static final Map<MouseButton, Integer> MOUSE_BUTTON_TO_JME = new HashMap<>();

    static {
        MOUSE_BUTTON_TO_JME.put(MouseButton.PRIMARY, BUTTON_LEFT);
        MOUSE_BUTTON_TO_JME.put(MouseButton.SECONDARY, BUTTON_RIGHT);
        MOUSE_BUTTON_TO_JME.put(MouseButton.MIDDLE, BUTTON_MIDDLE);
    }


    private int mouseX;
    private int mouseY;
    private int mouseWheel;


    @Override
    public void initialize() {
        initialize=true;
    }

    /**
     * 绑定fx 节点
     * @param fxPane
     */
    public void bind(Pane fxPane){
        fxPane.addEventHandler(MouseEvent.MOUSE_MOVED,this);
        fxPane.addEventHandler(MouseEvent.MOUSE_PRESSED,this);
        fxPane.addEventHandler(MouseEvent.MOUSE_RELEASED,this);
        //鼠标按下是MOUSE_DRAGGED事件，坑死
        fxPane.addEventHandler(MouseEvent.MOUSE_DRAGGED,this);
        FxMouseInput fxMouseInput=this;
        eventScrollHandler = new EventHandler<ScrollEvent>() {
            @Override
            public void handle(ScrollEvent event) {
                fxMouseInput.mouseWheelScroll(event);
            }
        };
        fxPane.addEventHandler(ScrollEvent.SCROLL,eventScrollHandler);
        this.fxPane=fxPane;
    }

    @Override
    public void update() {
        synchronized (mouseMotionEvents){
            // flush events to listener
            for (int i = 0; i < mouseMotionEvents.size(); i++){
                listener.onMouseMotionEvent(mouseMotionEvents.get(i));
            }
            mouseMotionEvents.clear();
        }
        synchronized (mouseButtonEvents){
            // flush events to listener
            for (int i = 0; i < mouseButtonEvents.size(); i++){
                listener.onMouseButtonEvent(mouseButtonEvents.get(i));
            }
            mouseButtonEvents.clear();
        }
    }

    @Override
    public void destroy() {
        fxPane.removeEventHandler(MouseEvent.MOUSE_MOVED,this);
        fxPane.removeEventHandler(MouseEvent.MOUSE_PRESSED,this);
        fxPane.removeEventHandler(MouseEvent.MOUSE_RELEASED,this);
        fxPane.removeEventHandler(ScrollEvent.SCROLL,eventScrollHandler);
        fxPane=null;
        initialize=false;
    }

    @Override
    public boolean isInitialized() {
        return initialize;
    }

    @Override
    public void setInputListener(RawInputListener listener) {
        this.listener=listener;
    }

    @Override
    public long getInputTimeNanos() {
        return System.nanoTime();
    }

    @Override
    public void setCursorVisible(boolean visible) {
            log.warn("setCursorVisible 不生效");
    }

    @Override
    public int getButtonCount() {
        return 3;
    }

    @Override
    public void setNativeCursor(JmeCursor cursor) {
        log.warn("cursor 不生效");
    }


    private void onCursorPos(double xpos, double ypos) {
        int xDelta;
        int yDelta;
        int x = (int) Math.round(xpos);
        int y = (int)fxPane.getHeight() - (int) Math.round(ypos);

        if (mouseX == 0) mouseX = x;
        if (mouseY == 0) mouseY = y;

        xDelta = x - mouseX;
        yDelta = y - mouseY;

        mouseX = x;
        mouseY = y;

        if (xDelta == 0 && yDelta == 0) return;

        final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(x, y, xDelta, yDelta, mouseWheel, 0);
        mouseMotionEvent.setTime(getInputTimeNanos());
        synchronized (mouseMotionEvents){
            mouseMotionEvents.add(mouseMotionEvent);
        }
    }
    private void onMouseButton(MouseEvent event, final boolean pressed) {
        final MouseButtonEvent mouseButtonEvent = new MouseButtonEvent(convertButton(event.getButton()), pressed, mouseX, mouseY);
        mouseButtonEvent.setTime(getInputTimeNanos());
        synchronized (mouseButtonEvents){
            mouseButtonEvents.add(mouseButtonEvent);
        }

    }
    private int convertButton(MouseButton button) {
        final Integer result = MOUSE_BUTTON_TO_JME.get(button);
        return result == null ? 0 : result;
    }


    @Override
    public void handle(MouseEvent event) {
        if (event.getEventType().equals(MouseEvent.MOUSE_MOVED)|| event.getEventType().equals(MouseEvent.MOUSE_DRAGGED)) {
            onCursorPos(event.getX(),event.getY());

        }
        if(event.getEventType().equals(MouseEvent.MOUSE_PRESSED)){
            onMouseButton(event,true);

        }
        if(event.getEventType().equals(MouseEvent.MOUSE_RELEASED)){
            onMouseButton(event,false);

        }
    }

    private void onWheelScroll(final double xOffset, final double yOffset) {
        mouseWheel += yOffset;
        final MouseMotionEvent mouseMotionEvent = new MouseMotionEvent(mouseX, mouseY, 0, 0, mouseWheel, (int) Math.round(yOffset));
        mouseMotionEvent.setTime(getInputTimeNanos());
        synchronized (mouseMotionEvents){
            mouseMotionEvents.add(mouseMotionEvent);
        }
    }

    public void mouseWheelScroll(ScrollEvent event) {
        onWheelScroll(event.getDeltaX() * WHEEL_SCALE, event.getDeltaY() * WHEEL_SCALE);
    }
}
